package ch.sv7.tool.xml;

import java.io.PrintWriter;

import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;

public class XMLDocumentWriter {

	PrintWriter out;

	public XMLDocumentWriter(PrintWriter out) {
		this.out = out;
	}

	public void close() {
		out.close();
	}

	public void write(Node node) {
		write(node, "");
	}

	public void write(Node node, String indent) {
		switch (node.getNodeType()) {
		case 9: // '\t'
			Document doc = (Document) node;
			out.println(indent + "<?xml version='1.0' encoding='iso-8859-1'?>");
			for (Node child = doc.getFirstChild(); child != null; child = child.getNextSibling())
				write(child, indent);

			break;

		case 10: // '\n'
			DocumentType doctype = (DocumentType) node;
			out.println("<!DOCTYPE " + doctype.getName() + ">");
			break;

		case 1: // '\001'
			Element elt = (Element) node;
			out.print(indent + "<" + elt.getTagName());
			NamedNodeMap attrs = elt.getAttributes();
			for (int i = 0; i < attrs.getLength(); i++) {
				Node a = attrs.item(i);
				out.print(" " + a.getNodeName() + "='" + fixupHtml(a.getNodeValue()) + "'");
			}

			Node child = elt.getFirstChild();
			if (child != null) {
				out.println(">");
				String newindent = indent + "    ";
				for (; child != null; child = child.getNextSibling())
					write(child, newindent);

				out.println(indent + "</" + elt.getTagName() + ">");
			} else {
				out.println("/>");
			}
			break;

		case 3: // '\003'
			Text textNode = (Text) node;
			String text = textNode.getData().trim();
			if (text != null && text.length() > 0)
				out.println(indent + fixupHtml(text));
			break;

		case 7: // '\007'
			ProcessingInstruction pi = (ProcessingInstruction) node;
			out.println(indent + "<?" + pi.getTarget() + " " + pi.getData() + "?>");
			break;

		case 5: // '\005'
			out.println(indent + "&" + node.getNodeName() + ";");
			break;

		case 4: // '\004'
			CDATASection cdata = (CDATASection) node;
			out.println(indent + "<" + "![CDATA[" + fixupEncoding(cdata.getData()) + "]]" + ">");
			break;

		case 8: // '\b'
			Comment c = (Comment) node;
			out.println(indent + "<!--" + c.getData() + "-->");
			break;

		case 2: // '\002'
		case 6: // '\006'
		default:
			System.err.println("Ignoring node: " + node.getClass().getName());
			break;
		}
	}

	public static String fixupHtml(String s) {
		if (s == null) {
			return "";
		}
		StringBuffer sb = new StringBuffer();
		int len = s.length();
		for (int i = 0; i < len; i++) {
			char c = s.charAt(i);
			switch (c) {
			default:
				sb.append(c);
				break;

			case 60: // '<'
				sb.append("&lt;");
				break;

			case 62: // '>'
				sb.append("&gt;");
				break;

			case 38: // '&'
				sb.append("&amp;");
				break;

			case 34: // '"'
				sb.append("&quot;");
				break;

			case 39: // '\''
				sb.append("&apos;");
				break;

			case 26: // '\032'
				sb.append("&apos;");
				break;
			}
		}

		return sb.toString();
	}

	public static String fixupEncoding(String s) {
		StringBuffer sb = new StringBuffer();
		int len = s.length();
		for (int i = 0; i < len; i++) {
			char c = s.charAt(i);
			switch (c) {
			default:
				sb.append(c);
				break;

			case 26: // '\032'
				sb.append("'");
				break;
			}
		}

		return sb.toString();
	}
}
